<?php
namespace Org\Weichat;

class Weichat{
	private $token;
	private $appid;
	private $appsecret;

	public function __construct($token,$appid,$appsecret){
		$this->token=$token;
		$this->appid=$appid;
		$this->appsecret=$appsecret;
	}

	public function valid($data)
    {
        $echoStr = $data["echostr"];
        //valid signature , option
        if($this->checkSignature($data))
        {
            // $file = dirname(THINK_PATH).'/Public/log.txt';
            // $str = $echoStr."\r\n";
            // file_put_contents($file,$str,FILE_APPEND);
            echo $echoStr;
            exit;
        }
    }

    public function responseMsg()
    {
        //get post data, May be due to the different environments
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

        //extract post data
        if (!empty($postStr)){
                /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
                   the best way is to check the validity of xml by yourself */
                libxml_disable_entity_loader(true);
                $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
                $fromUsername = $postObj->FromUserName;
                $toUsername = $postObj->ToUserName;
                $keyword = trim($postObj->Content);
                $time = time();
                $textTpl = "<xml>
                            <ToUserName><![CDATA[%s]]></ToUserName>
                            <FromUserName><![CDATA[%s]]></FromUserName>
                            <CreateTime>%s</CreateTime>
                            <MsgType><![CDATA[%s]]></MsgType>
                            <Content><![CDATA[%s]]></Content>
                            <FuncFlag>0</FuncFlag>
                            </xml>";             
                if(!empty( $keyword ))
                {
                    $msgType = "text";
                    $contentStr = "Welcome to wechat world!";
                    $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                    echo $resultStr;
                }else{
                    echo "Input something...";
                }

        }else {
            echo "";
            exit;
        }
    }
        
    private function checkSignature($data)
    {
        // you must define TOKEN by yourself
        if (!$this->token) {
            throw new Exception('TOKEN is not defined!');
        }
        
        $signature = $data["signature"];
        $timestamp = $data["timestamp"];
        $nonce = $data["nonce"];
                
        $token = $this->token;
        $tmpArr = array($token, $timestamp, $nonce);
        // use SORT_STRING rule
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );
        $file = dirname(THINK_PATH).'/Public/log.txt';
        $str = $tmpStr.'---'.$signature."\r\n";
        file_put_contents($file,$str,FILE_APPEND);
        if( $tmpStr == $signature ){
            return true;
        }else{
            return false;
        }
    }

    public function initData(){
        if ($_REQUEST ['doNotInit'])
            return true;
        
        $content = file_get_contents ( 'php://input' );
        ! empty ( $content ) || die ( '内容获取失败' );
        $data = new \SimpleXMLElement ( $content );
        $data || die ( '参数获取失败' );
        $file =  dirname(THINK_PATH).'/Uploads/log.txt';
        $str = $content."\r\n";
        file_put_contents($file,$str,FILE_APPEND);
        foreach ( $data as $key => $value ) {
            $this->data [$key] = strval ( $value );
        }
    }

     /* 获取微信平台请求的信息 */
    public function getData() {
        return $this->data;
    }

    /* ========================发送被动响应消息 begin================================== */
    /* 回复文本消息 */
    public function replyText($content) {
        $msg ['Content'] = $content;
        $this->_replyData ( $msg, 'text' );
    }
    /* 回复图片消息 */
    public function replyImage($media_id) {
        $msg ['Image'] ['MediaId'] = $media_id;
        $this->_replyData ( $msg, 'image' );
    }
    /* 回复语音消息 */
    public function replyVoice($media_id) {
        $msg ['Voice'] ['MediaId'] = $media_id;
        $msg ['Voice'] ['MediaId'] = $media_id;
        $this->_replyData ( $msg, 'voice' );
    }
    /* 回复视频消息 */
    public function replyVideo($media_id, $title = '', $description = '') {
        $msg ['Video'] ['MediaId'] = $media_id;
        $msg ['Video'] ['Title'] = $title;
        $msg ['Video'] ['Description'] = $description;
        $this->_replyData ( $msg, 'video' );
    }
    /* 回复音乐消息 */
    public function replyMusic($media_id, $title = '', $description = '', $music_url, $HQ_music_url) {
        $msg ['Music'] ['ThumbMediaId'] = $media_id;
        $msg ['Music'] ['Title'] = $title;
        $msg ['Music'] ['Description'] = $description;
        $msg ['Music'] ['MusicURL'] = $music_url;
        $msg ['Music'] ['HQMusicUrl'] = $HQ_music_url;
        $this->_replyData ( $msg, 'music' );
    }
    /*
     * 回复图文消息 articles array 格式如下： array( array('Title'=>'','Description'=>'','PicUrl'=>'','Url'=>''), array('Title'=>'','Description'=>'','PicUrl'=>'','Url'=>'') );
     */
    public function replyNews($articles) {
        $msg ['ArticleCount'] = count ( $articles );
        $msg ['Articles'] = $articles;
        
        $this->_replyData ( $msg, 'news' );
    }
    /* 发送回复消息到微信平台 */
    private function _replyData($msg, $msgType) {
        $msg ['ToUserName'] = $this->data ['FromUserName'];
        $msg ['FromUserName'] = $this->data ['ToUserName'];
        $msg ['CreateTime'] = NOW_TIME;
        $msg ['MsgType'] = $msgType;
        
        $xml = new \SimpleXMLElement ( '<xml></xml>' );
        $this->_data2xml ( $xml, $msg );
        $str = $xml->asXML ();
        
        // 记录日志
        //addWeixinLog ( $str, '_replyData' );
        
        echo ( $str );
    }

    /* 组装xml数据 */
    public function _data2xml($xml, $data, $item = 'item') {
        foreach ( $data as $key => $value ) {
            is_numeric ( $key ) && ($key = $item);
            if (is_array ( $value ) || is_object ( $value )) {
                $child = $xml->addChild ( $key );
                $this->_data2xml ( $child, $value, $item );
            } else {
                if (is_numeric ( $value )) {
                    $child = $xml->addChild ( $key, $value );
                } else {
                    $child = $xml->addChild ( $key );
                    $node = dom_import_simplexml ( $child );
                    $node->appendChild ( $node->ownerDocument->createCDATASection ( $value ) );
                }
            }
        }
    }
    /* ========================发送被动响应消息 end================================== */
    /* 上传多媒体文件 */
    public function uploadFile($file, $type = 'image', $acctoken = '') {
        $post_data ['type'] = $type; // 媒体文件类型，分别有图片（image）、语音（voice）、视频（video）和缩略图（thumb）
        $post_data ['media'] = $file;
        
        $url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=$acctoken&type=image";
        $ch = curl_init ();
        curl_setopt ( $ch, CURLOPT_POST, 1 );
        curl_setopt ( $ch, CURLOPT_URL, $url );
        curl_setopt ( $ch, CURLOPT_POSTFIELDS, $post_data );
        ob_start ();
        curl_exec ( $ch );
        $result = ob_get_contents ();
        ob_end_clean ();
        
        return $result;
    }
    /* 下载多媒体文件 */
    public function downloadFile($media_id, $acctoken = '') {
        // TODO
    }
    /**
     * GET 请求
     *
     * @param string $url           
     */
    private function http_get($url) {
        $oCurl = curl_init ();
        if (stripos ( $url, "https://" ) !== FALSE) {
            curl_setopt ( $oCurl, CURLOPT_SSL_VERIFYPEER, FALSE );
            curl_setopt ( $oCurl, CURLOPT_SSL_VERIFYHOST, FALSE );
        }
        curl_setopt ( $oCurl, CURLOPT_URL, $url );
        curl_setopt ( $oCurl, CURLOPT_RETURNTRANSFER, 1 );
        $sContent = curl_exec ( $oCurl );
        $aStatus = curl_getinfo ( $oCurl );
        curl_close ( $oCurl );
        if (intval ( $aStatus ["http_code"] ) == 200) {
            return $sContent;
        } else {
            return false;
        }
    }
    
    /**
     * POST 请求
     *
     * @param string $url           
     * @param array $param          
     * @return string content
     */
    private function http_post($url, $param) {
        $oCurl = curl_init ();
        if (stripos ( $url, "https://" ) !== FALSE) {
            curl_setopt ( $oCurl, CURLOPT_SSL_VERIFYPEER, FALSE );
            curl_setopt ( $oCurl, CURLOPT_SSL_VERIFYHOST, false );
        }
        if (is_string ( $param )) {
            $strPOST = $param;
        } else {
            $aPOST = array ();
            foreach ( $param as $key => $val ) {
                $aPOST [] = $key . "=" . urlencode ( $val );
            }
            $strPOST = join ( "&", $aPOST );
        }
        curl_setopt ( $oCurl, CURLOPT_URL, $url );
        curl_setopt ( $oCurl, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt ( $oCurl, CURLOPT_POST, true );
        curl_setopt ( $oCurl, CURLOPT_POSTFIELDS, $strPOST );
        $sContent = curl_exec ( $oCurl );
        $aStatus = curl_getinfo ( $oCurl );
        curl_close ( $oCurl );
        if (intval ( $aStatus ["http_code"] ) == 200) {
            return $sContent;
        } else {
            return false;
        }
    }

    public function setMenu($data){
        $access_token=$this->get_access_token();
        $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={$access_token}";
        $json_str = json_encode($data,JSON_UNESCAPED_UNICODE);
        $r =$this->post($url, $json_str);
        var_dump($r);
    }

    public function getUserInfo(){
        $code = isset($_GET['code']) ? $_GET['code'] : '';
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$this->appid."&secret=".$this->appsecret."&code={$code}&grant_type=authorization_code";
        $json = json_decode($this->get($url), true);
        $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$json['access_token']}&openid={$json['openid']}&lang=zh_CN";
        $user_info = json_decode($this->get($url), true);
        return $user_info;
    }

    public function get_access_token() {
        $this->cache_file = RUNTIME_PATH.'/cache_data/access_token.cache';
        $need_refresh = true;
        $json = null;
        
        if (file_exists($this->cache_file)) {
            $json = json_decode(file_get_contents($this->cache_file), true);
            if ($json['expires_to'] - time() > 60) {
                $need_refresh = false;
            }
        }
        
        if ($need_refresh) {
            $json = $this->get_remote_access_token();
            file_put_contents($this->cache_file, json_encode($json));
        }
        
        return $json['access_token'];
    }
    
    private function get_remote_access_token() {
        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$this->appid.'&secret=' . $this->appsecret;
        //TODO 网络错误处理
        $content = $this->get($url);
        $json = json_decode($content, true);
        $json['expires_to'] = time() + $json['expires_in'];
        
        return $json;
    }

    public function get($url) {
        $opts = array(   
            'http'=>array(  
                'method' => "GET",   
                'timeout' => 5,
            )
        );
        
        $cnt = 0;     
        while($cnt < 3 && ($content = file_get_contents($url, false, stream_context_create($opts))) === FALSE) $cnt++;
        
        return $content;
    }
    
    public function post($url, $post = null){
        
        $content = is_array($post) ? http_build_query($post, '', '&') : $post;
        
        $opts['http'] = array (
            'timeout' => 5,
            'method' => 'POST',
            'content' => $content,
            'header' => "Content-Type: application/x-www-form-urlencoded\r\n".
            "Content-Length: ".strlen($content)."\r\n",
         );
        
        $cnt = 0;     
        while($cnt < 3 && ($content = file_get_contents($url, false, stream_context_create($opts))) === FALSE) $cnt++;   
        
        return $content;
    }
}